#include <time.h>

#include "GTimer.h"
#include "GLog.h"
#include "GParallel.h"
#include "GSystem.h"
#include "GThread.h"
#include "GTestApp.h"
#include "GTaskManager.h"
#include "GTransform3.h"

using namespace GEngine;

/************************************************/
/*					MultiplyTask				*/
/************************************************/

MultiplyTask::MultiplyTask(GMath::GMatrix4x4* ViewProj, GMath::GMatrix4x4* OutputArr)
{
	srand((unsigned int)time(NULL));

	this->OutputArr = OutputArr;
	this->ViewProj = ViewProj;
}

MultiplyTask::~MultiplyTask()
{
}

void MultiplyTask::CreateRandom(INOUT GMath::GMatrix4x4* Matrix)
{
	for (GSize I = 0; I < 16; ++I)
	{
		Matrix->Arr[I] = (float)(rand() % 16) + 1.0f;
	}
}

void MultiplyTask::Execute(GSize Index, GSize WorkerId)
{
	// World matrices
	GMath::GMatrix4x4 Scale;
	GMath::GMatrix4x4 Rotation;
	GMath::GMatrix4x4 Position;

	// Create randomly
	CreateRandom(&Scale);
	CreateRandom(&Rotation);
	CreateRandom(&Position);

	GMath::GMatrix4x4 Temp = (*ViewProj) * Position * Rotation * Scale;
	GMath::GMatrix4x4::Inverse(OutputArr[Index], Temp);
}

/************************************************/
/*					RootTask					*/
/************************************************/

RootTask::RootTask(GEngine::GTaskResult* TaskResult, GEngine::GTaskPriority TaskPriority, bool TaskThreadSafe)
	: GTask(TaskResult, TaskPriority, TaskThreadSafe)
{
	srand((unsigned int)time(NULL));

	for (GSize I = 0; I < 16; ++I)
	{
		ViewProj.Arr[I] = (float)(rand() % 16);
	}
}

RootTask::~RootTask()
{
}

GUInt32	RootTask::Execute(GSize WorkerId)
{
	for (GSize I = 0; I < UPDATE_COUNT; ++I)
	{
		GTaskRange Range(0, UPDATE_MATRIX_COUNT, UPDATE_GRANULARITY);
		MultiplyTask UpdateTask(&ViewProj, Outputs);

		GParallel::For(&UpdateTask, Range);
	}
	
	return 0;
}

/************************************************/
/*					GTestApp					*/
/************************************************/

G_MODULE_DESCRIPTOR(GTestApp, GEngine::GApplication, GEngine::GApplication);

GTestApp::GTestApp()
{	
	FrameId = 0;
}

GTestApp::~GTestApp()
{
}

bool GTestApp::Update(float ElapsedTime)
{
	GEngine::GTaskResult Result;
	RootTask Task(&Result, GEngine::G_TP_DEFAULT, false);

	GUtil::GTimer Timer;

	mGTaskManager->Schedule(&Task);
	mGTaskManager->BusyWait(&Result);

	RootTimes[FrameId] = Timer.GetElapsedSeconds();
	FrameId++;

	if (FrameId == G_FRAME_COUNTS)
	{
		double Sum = 0;
		for (GSize I = 0; I < G_FRAME_COUNTS; ++I)
		{
			Sum += RootTimes[I];
		}

		double average = Sum / (float)G_FRAME_COUNTS;

		mGFileLogger->Log(G_LOG_TYPE_INFO, "Average time is %lf seconds.", average);

		FrameId = 0;
	}
	
	return true;
}

bool GTestApp::Initialize()
{
	if (!GApplication::Initialize())
	{
		return false;
	}

	return true;
}

void GTestApp::Uninitialize()
{
	GApplication::Uninitialize();
}